// mac osx 10.5.8
// see test1.groovy for sample code using ant copy
// contains or has logic
import java.io.*;
import java.nio.channels.*;
import java.util.zip.*;


import javax.swing.*
import java.io.*;
import java.util.*
import java.text.SimpleDateFormat

def fs = System.getProperty('file.separator')
def title =  "// ---------------------------------------------------------------"
int BUFFER = 2048;
def TEMPORARYNAME = "._harvester.tmp"

def ant = new AntBuilder()		// construct ant builder instance
def ct = 0				// counter for number of selected resources
def rd = 0				// counter for total number of processed resources
def fn 					// temporary f.name variable
HarvesterHelper hh			// support module
def debugFlag = false
// ======================================
// load gui properties from prior session
fn = 'harvester.properties'
def config 
File fp = new File(fn)
if (fp.exists())
{
	config = new ConfigSlurper().parse(fp.toURL())
} // end of if to load properties
else
{
	int messageType = JOptionPane.ERROR_MESSAGE; 
	JOptionPane.showMessageDialog(null, "The $fn configuration file cannot be located", "No Configuration Error", messageType)
	System.exit(0);
} // end of else

def listpath = config.path.to.target		// vaultOutputPath
def thispath = config.path.to.source		// path to read from

thispath = (thispath.endsWith(fs)) ? thispath : thispath + fs ;	// adjusted donor path to input
listpath = (listpath.endsWith(fs)) ? listpath : listpath + fs ;	// adjusted target path to output



def includeStrategy = 1				// -1 to match from start; 0 to match anywhere; 1 to match fragment to end
def omitStrategy = 1				// -1 to match from start; 0 to match anywhere; 1 to match fragment to end
/*
	Open an audit trail text file to write names of harvested resources
*/
def listfile = listpath+"list.txt"		// vaultOutputFileList
def list = new File(listfile)			// file handle for the text file holding names of files copied to the vaultOutputPath 

def sayaudit = {fh,text -> fh.append(text+'\n'); }

// short method to print audit trail messages
def say =
{ text ->
	//if (true) println text;
	if (debugFlag) sayaudit(list,text)
}	// end of method		


def today = new Date()
def df = new SimpleDateFormat('EEE, dd MMM yyyy HH:mm:ss z')
def tt = df.format(today.time)

// load configuration settings
def searchZipsFlag = config.cb1			// include all resources found within zip compressed files
def allowDupsFlag = config.cb2			// forces serial number incrementing of resources with identical names
def removeResourceFlag = config.cb3		// after a successful capture of a resource to it's new home folder, remove original source
debugFlag = config.cb4			// cause more info to be written to list.txt audit trail thats kept with each output folder written to

// more debug text written here
say title
say "date := ${tt}"
say "donor path is $thispath"
say "target path is $listpath"

// includes
def includeFlag = config.cbpa			// include all resources regardless of choice of fragments
def includeIgnoreCase = config.cbpic		// false if fragment must match case exactly, or true to convert to lowercase and match all
def includeList					// = config.includes.split(';').toList()  	
						// [""]     //".xml",".jpg",".groovy",".java",".txt",".src",".fred"]

if (config.bgis) includeStrategy = -1
if (config.bgih) includeStrategy = 0
if (config.bgos) omitStrategy = -1
if (config.bgoh) omitStrategy = 0

// set case of text for matching fragments
if (includeIgnoreCase)
{
	includeList = config.includes.toLowerCase().split(';').toList()
}
else
{
	includeList = config.includes.split(';').toList()
} // end of if


// omits
def omitFlag = config.cboa			// omit none flag
def omitIgnoreCase = config.cboic		// flag to ignore case of match; everything is lowercase unless
def omitList 					// = config.omits.split(';').toList()   	
						// [".pdf",".src",".txt"]
if (omitIgnoreCase)				// if true, convert all search fragments to lowercase
{
	omitList = config.omits.toLowerCase().split(';').toList()
}
else						// take case as declared in gui
{
	omitList = config.omits.split(';').toList()
} // end of if

// ==========================================================
// closure to write zipped inout stream to hard disk
write = {zis -> 
        def tot = 0
        try 
        {
         BufferedOutputStream dest = null;
         def count = 0
         def data = new byte[BUFFER];
         // write the files to the disk
         FileOutputStream fos = new FileOutputStream(listpath+TEMPORARYNAME);
         dest = new BufferedOutputStream(fos, BUFFER);
         while ((count = zis.read(data, 0, BUFFER)) != -1) 
         {
        	dest.write(data, 0, count);
        	tot+=count
         } // end of while
        	            
         dest.flush();
         dest.close();
        } // end of try 
        catch (IOException e) 
        {
            println "zip copy:"+e.message
            throw e;
        } // end of catch
        
        return tot
} // end of write
//========================================================

// ==========================================================
// closure to perform what we want to do, now that decisions have been made
unzip = {fi -> 
         BufferedOutputStream dest = null;
         FileInputStream fis = new FileInputStream(fi);
         ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
         ZipEntry entry;
         say "--- zip file:"+fi.toString()
         
         // read each entry from zip file and see if it's needed'
         while((entry = zis.getNextEntry()) != null) 
		 {
		    //say entry.getName()
		    def f = new File(entry.getName())
		    f.setLastModified(entry.getTime())
		    
		    ++rd
		    
            // does this resource name contain the partial text fragment we want ?
		    if (( includeFlag | hasAllowableFragment(f) ) && 
		        ( omitFlag | !hasOmitableFragment(f) ))
		    {
		        say "... selected "+entry.getName()+" and size="+entry.getSize()+" compressed= " +    entry.getCompressedSize()
	        	i = entry.getName().lastIndexOf(fs) + 1
            	def name = entry.getName().substring(i)
            	def fn2 = listpath+name
            	say "fn2 starts with = $fn2"
                //?? harvest helper not correctly find version of zip entry even if date and size are =
            	fn2 = hh.getName(f,fn2,entry.getSize())
    	        say "hh.getName() returned a name of "+fn2
            	say "allowDupsFlag=$allowDupsFlag  and hh.isDup()=${hh.isDup()}"
                if ( (allowDupsFlag & hh.isDup()) | (!hh.isDup()) )
                {
                    try 
                    {
                		say "... copying $fn2"
	    	            //File f2 = new File(fn2)
	    	            Date lastModified = new Date(entry.getTime())
	    	            ct++				// bump processed count

                        // write zip entry to listpath+TEMPORARYNAME = /media/DURACELL/tmp/._harvester.tmp
                        written = write(zis)	
                        
                        say "... writing filesize of "+written+'\n'                        

	    	            def metadata = "['key':'$name', 'size':'${written}', 'date':'${lastModified}', 'from':'${fi.canonicalPath.replaceAll("\'","_")}(${entry.getName()})', 'to':'${fn2}']";
	    	            list.append(metadata+'\n')

                        File tmp = new File(listpath+TEMPORARYNAME)
		        		File ouf = new File(fn2);
		        		tmp.renameTo(ouf)
		             	boolean blnSuccess = ouf.setLastModified(entry.getTime());
                    } // end of try 
                    
                    catch (IOException e) 
                    {
                        println "zip copy:"+e.message
                        throw e;
                    } // end of catch
                    
	        	} // end of if
	        } // end of if
		 } // end of while
    return true
} // end of closure

// ==========================================================
// closure to perform what we want to do, now that decisions have been made
doit = {f -> 
	assert f instanceof File	
	say "doit -> '${f.canonicalPath}' "
	fn = f.toString()		// get full filename
	i = fn.lastIndexOf(fs) + 1
	def name = fn.substring(i)

	def fn2 = listpath+name
    	say "fn2 starts with = $fn2"
    	fn2 = hh.getName(f,fn2,f.size())
    	say "hh.getName() returned a name of "+fn2

	say "allowDupsFlag=$allowDupsFlag  and hh.isDup()=${hh.isDup()}"

	if ( (allowDupsFlag & hh.isDup()) | (!hh.isDup()) )
        try 
        {
		say "... copying $fn2"
		File f2 = new File(fn2)
    		hh.localcopy(f,f2)
		Date lastModified = new Date(f.lastModified())
		ct++				// bump processed count
		def metadata = 
		"['key':'$name', 'size':'${f.size()}', 'date':'${lastModified}', 'from':'${f.canonicalPath.replaceAll("\'","_")}', 'to':'${fn2}']";
		list.append(metadata+'\n')

		if (removeResourceFlag)
		{
			say "// removing ${f.canonicalPath}"
			f.delete()
		} // end of if
        } 
        catch (IOException e) 
        {
            println "localcopy:"+e.message
            throw e;
        }

	return true;

} // end of closure


// =======================================================
// closure to see if resource name is in list of allowable fragments
hasAllowableFragment = { f ->

	def flag = false
	fn = (includeIgnoreCase) ? f.name.toLowerCase() : f.name;

	includeList.each() {e ->
		String s = (String)e
		if ( s.length()>0) 
		{
			switch(includeStrategy)
			{
				case -1 :	if (fn.startsWith(s)) {flag=true;}  // say "includeStrategy=$includeStrategy fn=$fn s=$s";}
				case  0 :	if (fn.indexOf(s) > 0) {flag=true;} // say "includeStrategy=$includeStrategy fn=$fn s=$s";}
				case  1 :	if (fn.endsWith(s)) {flag=true;}    // say "includeStrategy=$includeStrategy fn=$fn s=$s";}
			}
		} // end of if
	} // end of each
	
	return flag
}  // end of closure



// =======================================================
// closure to see if resource name is a compressed zip file
isZip = { f ->
	return (searchZipsFlag & f.name.toLowerCase().endsWith(".zip"))
}  // end of closure


// =======================================================
// closure to see if resource name is in list of fragments to be ignored
hasOmitableFragment = { f ->

	def flag = false
	fn = (omitIgnoreCase) ? f.name.toLowerCase() : f.name;

	omitList.each() {e ->
		String s = (String)e
		if ( s.length()>0) 
		{
			switch(omitStrategy)
			{
				case -1 : if (fn.startsWith(s)) {flag=true;}  // say "omitStrategy=$omitStrategy fn=$fn s=$s so ignored";}
				case  0 : if (fn.indexOf(s) > 0) {flag=true;}  // say "omitStrategy=$omitStrategy fn=$fn s=$s so ignored";}
				case  1 : if (fn.endsWith(s)) {flag=true;}  // say "omitStrategy=$omitStrategy fn=$fn s=$s so ignored";}
			}
		} // end of if
	} // end of each
	
	return flag
}  // end of closure



// ===================================
// execution code starts here :
// ===================
// start of code

hh = new HarvesterHelper()

say title
say "source := <$thispath>"
say "target := <$listpath>"
say "include:= Flag is set to <$includeFlag> allowing these items:<$includeList> with case of <$includeIgnoreCase>"
say "omit   := Flag is set to <$omitFlag> omitting these items:<$omitList> with case of <$omitIgnoreCase>"

// now lets iterate over fileset
ct = 0
boolean ok = true

def inputpath = new File(thispath)
inputpath.eachFileRecurse  
{ f ->
	++rd
	if (!f.toString().startsWith(listpath))
	{
		// does this resource name contain the partial text fragment we want ?
		if (( includeFlag | hasAllowableFragment(f) | isZip(f) ) && 
		    ( omitFlag | !hasOmitableFragment(f) ))
		{
		    ok = (isZip(f)) ? unzip(f) :  doit(f)
		} // end of if
	} // end of else

} // end of for scanner

// pump out stats
def ctx = (ct==0) ? "no": ct
def rtx = (rd==0) ? "no": rd
def tx = "Copied $ctx matching resources out of $rtx total resources"
say tx
say title
say " "
println tx
return tx
/*		// spare code
		//  sleep(300)
  		//  ( new AntBuilder ( ) ).copy ( file : f , tofile : ou )

*/
